
import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.*;


/**
 * Class containing all the information describing a server
 */
class ServerInfo {
	String name;
	String ip;
	int av_bw;

	public ServerInfo (String name, String ip, int start_bw) {
            this.name = name;
            this.ip = ip;
            this.av_bw = start_bw;
	}
}

/**
 * This class sets up an interface between File servers and the download 
 * server. It checks file server if it contains the application and sends
 * back the available bandwidth of file server to the download server if
 * applikation is available. If not 0 is sent back.
 */
class intface extends Thread {
    private DSFS dsfs; //
    private Registry registry;
    private String AppName;
    private boolean doneRead;
    private int bw = 0;

    public intface (String AppName, String serverName, String ip) {
        try {
            this.registry = LocateRegistry.getRegistry(ip);
            this.dsfs = (DSFS) registry.lookup(serverName);
            this.AppName = AppName;

        }
        catch (Exception e) {
            System.err.println("DS to FS interface has exception: "
                    + e.toString());
        }
    }

    // Interface waiting for DS to get BW
    private synchronized void waiting() throws InterruptedException {
        wait();
    }

    // Method for getting bandwidth from 
    public synchronized int getbw() {
        if(doneRead) {
            notify();
            return bw;
        }
        else {
            return -1;
        }
    }

    @Override
    public void run() {
        try {
            bw = dsfs.getAppBW(AppName);
            doneRead = true;
            waiting();
        }
        catch (Exception e) {
            System.err.println("DS to FS interface has exception: "
                    + e.toString());
        }
    }
}

/**
 * Download Server receives a request from Client class and sends
 * request for bandwidth from different File servers. When all file
 * servers have replied, or a timeout is reached, the download
 * server sends back the
 *
 * @author s072069
 */
public class DS implements ClientDS {
    private int[] bw;
    private int timeout = 500; // 5 seconds
    private int checkrate = 50; // The rate which is being checked
                                // for interrupt
    private Vector<ServerInfo> servers = new Vector<ServerInfo>();
                    // database of servers
    private Timer t; // Timer process
    private int replies;
    private ServerInfo maxServer;
    private intface[] interfaces;

    public DS() {
        // Database
        servers.add(new ServerInfo("FS0", "192.168.0.100", 0));
        servers.add(new ServerInfo("FS1", "192.168.0.100", 0));
        servers.add(new ServerInfo("FS2", "192.168.0.101", 0));
        servers.add(new ServerInfo("FS3", "192.168.0.101", 0));
        servers.add(new ServerInfo("FS4", "192.168.0.102", 0));
        servers.add(new ServerInfo("FS5", "192.168.0.102", 0));

        this.bw = new int[servers.size()];

        // inirtialize array of bandwidths to zero
        for(int i = 0; i < servers.size(); i++) {
            bw[i] = 0;
        }
    }

    public synchronized void setBW(int index) {
        int temp = interfaces[index].getbw();

        if (temp >= 0) {
            bw[index] = temp;
            replies++;
        }

        if(replies == servers.size()) {
            t.interrupt();
        }
    }

    public synchronized void connectFS(String AppName) {
        replies = 0;
        t = new Timer(timeout);
        maxServer = new ServerInfo("none","none", 0);
        t.start();

        try {
           // array containing interfaces to file servers
           interfaces = new intface[servers.size()];

           // the interfaces to fileservers are started
           for(int i = 0; i < servers.size(); i++) {

               interfaces[i] = new intface(AppName,
                       servers.elementAt(i).name,
                       servers.elementAt(i).ip);
               interfaces[i].start();
           }


           while(replies < servers.size()) {
               wait(checkrate);
               for(int i = 0; i < servers.size(); i++) setBW(i);
               if(t.checktimer()) break; // break if timeout
           }
           replies = 0;
           // Maximum available bandwidth is determined
           for(int i = 0; i < bw.length; i++) {
                int temp = bw[i];
                bw[i] = 0; // Array with bandwidths is reset

                if (maxServer.av_bw < temp) {
                    maxServer.av_bw = temp;
                    maxServer.name = servers.elementAt(i).name;
                    maxServer.ip = servers.elementAt(i).ip;
                }
           }
         }
        catch (Exception e) {
            System.err.println("Download server exception: "
                    + e.toString());
        }
    }

    /**
     * Method used by Client to connect to download server and
     * requesting an application.
     */
    public int getApp(String AppName) throws RemoteException {
        connectFS(AppName);
        return maxServer.av_bw;
    }

    /**
     * Method used by Client to get the host name of the file
     * server
     */
    public String getIP() throws RemoteException {
        return maxServer.ip;
    }

    public static void main(String args[]) {

        try {
            // Setting up server connection to Client
            DS DLserver = new DS();
            ClientDS stub = (ClientDS) UnicastRemoteObject.
                    exportObject(DLserver, 0);

            Registry reg = LocateRegistry.getRegistry();
            reg.rebind("ClientDS", stub);

            System.err.println("Download server ready");

        } catch (Exception e) {
            System.err.println("Download server exception: "
                    + e.toString());
        }
    }
}
